﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Slippor.Douban.Radio.Assist;
using Slippor.Douban.Radio.Filter;
using Slippor.Utility.WebWorker;

namespace Slippor.Douban.Radio.BaseForm
{
    public partial class DoubanForm : Form
    {
        #region 字段
        protected Song _currentSong = Song.DefaultSong;
        protected RadioState _currentState = RadioState.Normal;
        protected RadioType _currentType = RadioType.PrivateRadio;
        protected RadioVersion _currentVersion = RadioVersion.CurrentVersion;
        protected bool _isPlaying;

        protected IFilter _normalFilter = new NormalFilter();
        protected IntPtr _wbHandle = IntPtr.Zero;

        public RadioState CurrentState
        {
            get { return _currentState; }
        }

        public RadioVersion CurrentVersion
        {
            get { return _currentVersion; }
        }
        #endregion

        public DoubanForm()
        {
            InitialMessageFilter();
            InitializeComponent();
            Shown += Main_Shown;
            Closing += DoubanForm_Closing;
            Closed += Main_Closed;
            InitializeMiniNormal();
            InitializeForm();
            Application.Idle += new EventHandler(Application_Idle);
        }

        private MessageFilter _msgFliter;
        private void InitialMessageFilter()
        {
            _msgFliter = new MessageFilter(_wbHandle)
                             {
                                 FilterClick = RedirectClick
                             };
            Application.AddMessageFilter(_msgFliter);
        }

        private void Application_Idle(object sender, EventArgs e)
        {
            if (_getSongInfo)
            {
                _getSongInfo = false;
                GetSongInfo();
            }
        }

        private void InitializeForm()
        {
            SetPosition();
            InitialFields();
            InitialControls();
            InitialServices();
            InitialProperties();
        }

        protected virtual void InitialFields()
        {
        }

        private void VersionCheckEnd(RadioVersion newVersion)
        {
            if (newVersion.IsNewer(_currentVersion))
            {
                DialogResult result = MessageBox.Show(String.Format(StringResource.Message_UpdateAlert, Environment.NewLine + newVersion.VersionDes), StringResource.Message_NewVersion, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
                if (result == DialogResult.Yes)
                {
                    RadioUtility.Browse(String.Format("{0}?s=download&v={1}&t={2}", newVersion.DownloadUrl, CurrentVersion.VersionCode, CurrentVersion.VersionMark));
                }
            }
            else
            {
                _currentVersion.IsLatest = true;
            }
        }

        private void ConfirmHandler()
        {
            if (_wbHandle == IntPtr.Zero)
            {
                _wbHandle = RadioUtility.GetHandler(webBrowserDouban.Handle, "Internet Explorer_Server");
                _msgFliter.Handler = _wbHandle;
            }
        }

        private static FavState CheckFav(IntPtr hdc)
        {
            if (hdc != IntPtr.Zero)
            {
                int v;
                v = RadioUtility.GetPixel(hdc, 267, 147);
                Color color = Color.FromArgb(v);
                if (color.A == 255)
                {
                    return FavState.Unknown;
                }
                if (Math.Abs(color.R - color.B) > 100)
                {
                    return FavState.Fav;
                }
                return FavState.UnFav;
            }
            return FavState.Unknown;
        }

        private RadioType CheckType(IntPtr hdc)
        {
            if (hdc != IntPtr.Zero)
            {
                int v = RadioUtility.GetPixel(hdc, 330, 145);
                Color color = Color.FromArgb(v);
                if (color.A == 255 || color.B < 150)
                {
                    return RadioType.Unknown;
                }
                if (color.B < 150)
                {
                    return RadioType.PrivateRadio;
                }
                return RadioType.PublicRadio;
            }
            return RadioType.Unknown;
        }

        #region EventHandle
        private bool _reactive = false;
        private bool _activeshow = false;
        private void InitializeMiniNormal()
        {
            SizeChanged += Main_SizeChanged;
            Activated += DoubanForm_Activated;
            Deactivate += DoubanForm_Deactivate;
            _reactiveTimer.Tick += (senderT, eT) =>
            {
                _reactiveTimer.Stop();
                Activate();
            };
        }

        private void DoubanForm_Activated(object sender, EventArgs e)
        {
            if(_activeshow)
            {
                _activeshow = false;
            }
        }

        private Timer _reactiveTimer = new Timer()
        {
            Interval = 100,
        };

        private void DoubanForm_Deactivate(object sender, EventArgs e)
        {
            if(_reactive)
            {
                _reactive = false;
                _reactiveTimer.Start();
            }
        }

        private void Main_SizeChanged(object sender, EventArgs e)
        {
            switch (WindowState)
            {
                case FormWindowState.Minimized:
                    if (_currentState == RadioState.Minimized)
                    {
                        _currentState = RadioState.TempNormal;
                        WindowState = FormWindowState.Normal;
                        ShowMainWindow();
                        _currentState = RadioState.Normal;
                        _reactive = TopMost;
                    }
                    else if (_currentState == RadioState.Normal)
                    {
                        TopMost = false;
                        _currentState = RadioState.TempMinimized;
                        WindowState = FormWindowState.Normal;
                        HideMainWindow();
                        _currentState = RadioState.Minimized;
                    }
                    break;
                case FormWindowState.Normal:
                    break;
            }
        }

        protected const int _strictWidth = 420;
        protected const int _strictHeight = 186;
        private void Main_Shown(object sender, EventArgs e)
        {
            if (webBrowserDouban.Height > _strictHeight)
            {
                Height -= webBrowserDouban.Height - _strictHeight;
                webBrowserDouban.Height = _strictHeight;
            }
            if (webBrowserDouban.Width > _strictWidth)
            {
                Width -= webBrowserDouban.Width - _strictWidth;
                webBrowserDouban.Width = _strictWidth;
            }
            if (EnableHotKey)
            {
                RegHotkeys();
            }
            if (AutoMin)
            {
                HideToNotify();
            }
            Navigate();
            OnDisplayed();
        }

        private void Navigate()
        {
            webBrowserDouban.Navigate(StaticResources.RadioUrl);
        }

        private void Main_Closed(object sender, EventArgs e)
        {
            Application.Idle -= Application_Idle;
            UnHotkeys();
            SavePosition();
            Drop(_currentSong);
            TrackStop();
            OnClosed();
        }

        protected virtual void OnDisplayed()
        {
            //解决开机启动有时候不显示托盘图标的问题。
            notifyIcon.Visible = false;
            notifyIcon.Visible = true;
        }

        protected virtual void OnClosed()
        {
        }
        #endregion

        #region 保存位置
        private void SetPosition()
        {
            Point lastPosition = _settings.Position;
            if (lastPosition.X != 0 || lastPosition.Y != 0)
            {
                this.StartPosition = FormStartPosition.Manual;
                if (lastPosition.X > 0 && lastPosition.X + Width < Screen.PrimaryScreen.WorkingArea.Width)
                {
                    Left = lastPosition.X;
                }
                else if (lastPosition.X < 0)
                {
                    Left = 0;
                }
                else if (lastPosition.X + Width >= Screen.PrimaryScreen.WorkingArea.Width)
                {
                    Left = Screen.PrimaryScreen.WorkingArea.Width - Width;
                }
                if (lastPosition.Y > 0 && lastPosition.Y + Height < Screen.PrimaryScreen.WorkingArea.Height)
                {
                    Top = lastPosition.Y;
                }
                else if (lastPosition.Y < 0)
                {
                    Top = 0;
                }
                else if (lastPosition.Y + Height >= Screen.PrimaryScreen.WorkingArea.Height)
                {
                    Top = Screen.PrimaryScreen.WorkingArea.Height - Height;
                }
            }
        }

        private void SavePosition()
        {
            Point point;
            if (_currentState == RadioState.Normal)
            {
                point = new Point(Left, Top);
            }
            else
            {
                point = _originalLocation;
            }
            if (point.X == 0)
            {
                point.X = -1;
            }
            else if (point.Y == 0)
            {
                point.Y = -1;
            }
            _settings.Position = point;
            _settings.Save();
        }
        #endregion

        #region HotKey
        private bool DifUnFav
        {
            get
            {
                ICommand commander = CommandFactory.GetCommand(Command.UnFav, RadioType.PrivateRadio);
                return !String.IsNullOrEmpty(StaticResources.SettingManager.GetString(commander.ID));
            }
        }


        private readonly List<Command> _hkCommands = new List<Command>();
        private Dictionary<Command, ToolStripMenuItem> _commands = new Dictionary<Command, ToolStripMenuItem>();

        internal void RegHotkeys()
        {
            _hkCommands.Clear();
            _commands = new Dictionary<Command, ToolStripMenuItem>
                            {
                                {Command.Control, toolStripMenuItemControl},
                                {Command.Fav, toolStripMenuItemFav},
                                {Command.UnFav, null},
                                {Command.Next, toolStripMenuItemNext},
                                {Command.Del, toolStripMenuItemDel},
                                {Command.Search, null},
                                {Command.Notify, toolStripMenuItemShow},
                                {Command.ShowInfo, null},
                            };
            foreach (Command command in _commands.Keys)
            {
                ICommand commander = CommandFactory.GetCommand(command, RadioType.PrivateRadio);
                if (!String.IsNullOrEmpty(StaticResources.SettingManager.GetString(commander.ID)))
                {
                    RegHotKey(StaticResources.SettingManager.GetString(commander.ID), command, _commands[command]);
                }
            }
            _hotKeyService.Handle = Handle;
            _hotKeyService.RegAll();
        }

        private void RegHotKey(String hotKey, Command command, ToolStripMenuItem item)
        {
            if (HotKey.RegisterHotKey(Handle, hotKey, command))
            {
                if (item != null)
                {
                    if (item.ShortcutKeyDisplayString != hotKey)
                    {
                        item.ShortcutKeyDisplayString = hotKey;
                    }
                    item.ShowShortcutKeys = true;
                }
                _hkCommands.Add(command);
            }
        }

        internal void UnHotkeys()
        {
            foreach (Command command in _hkCommands)
            {
                UnHotKey(command, _commands[command]);
            }
            _hotKeyService.Handle = Handle;
            _hotKeyService.RemoveAll();
        }

        private void UnHotKey(Command command, ToolStripMenuItem item)
        {
            if (item != null)
            {
                item.ShowShortcutKeys = false;
            }
            HotKey.UnregisterHotKey(Handle, (int)command);
        }

        #endregion

        #region 执行命令

        public bool RedirectClick(int x, int y)
        {
            ConfirmHandler();
            IntPtr hdc = RadioUtility.GetDC(_wbHandle);
            //RadioType radioType = CheckType(hdc);
            RadioType radioType = _currentType;
            RadioUtility.ReleaseDC(_wbHandle, hdc);
            if (_isPlaying)
            {
                _currentType = radioType;
                if (y <= 165 && y >= 125)
                {
                    _targetSong = _currentSong;
                    if (x <= 285 && x >= 245)
                    {
                        OnExecute(Command.Fav);
                    }
                    if (x <= 340 && x >= 300)
                    {
                        if (_currentType != RadioType.PublicRadio)
                        {
                            OnExecute(Command.Del);
                        }
                    }
                    if (x <= 390 && x >= 350)
                    {
                        OnExecute(Command.Next);
                    }
                }
                else if (y >= 55 && y <= 95 && x >= 180 && x <= 370)
                {
                    OnExecute(Command.Control);
                }
            }
            else if (x > 0 && x <= 420 && y > 0 && y <= 190)
            {
                OnExecute(Command.Control);
            }
            return false;
        }

        protected override void WndProc(ref Message message)
        {
            const int WM_HOTKEY = 0x0312;
            //按快捷键 
            switch (message.Msg)
            {
                case WM_HOTKEY:
                    switch (message.WParam.ToInt32())
                    {
                        case (int)Command.Control:
                        case (int)Command.Del:
                        case (int)Command.Next:
                        case (int)Command.Filter:
                            ExcuteCommand(CommandFactory.GetCommand((Command)message.WParam.ToInt32(), _currentType));
                            break;
                        case (int)Command.Fav:
                            if (!DifUnFav || _currentSong.FaveState != FavState.Fav)
                            {
                                ExcuteCommand(CommandFactory.GetCommand((Command)message.WParam.ToInt32(), _currentType));
                            }
                            break;
                        case (int)Command.UnFav:
                            if (_currentSong.FaveState == FavState.Fav)
                            {
                                ExcuteCommand(CommandFactory.GetCommand((Command)message.WParam.ToInt32(), _currentType));
                            }
                            break;
                        case (int)Command.Notify:
                            if (_currentState == RadioState.Notify)
                            {
                                _currentState = RadioState.Normal;
                                ShowFromNotify();
                            }
                            else
                            {
                                HideToNotify();
                            }
                            break;
                        case (int)Command.Search:
                            DefaultSearch.Search(_currentSong.Title);
                            break;
                        case (int)Command.ShowInfo:
                            PopInfo();
                            break;
                        default:
                            _hotKeyService.Check(message.WParam.ToInt32());
                            break;
                    }
                    break;
                default:

                    break;
            }
            base.WndProc(ref message);
        }

        #endregion

        #region 记录历史
        private void TrackHistory(Song song)
        {
            using (var trackWorker = new BackgroundWorker())
            {
                trackWorker.DoWork += (sender, e) =>
                                          {
                                              _trackerService.Track(song);
                                          };
                trackWorker.RunWorkerCompleted += (sender, e) =>
                    {
                        if (_trackerService.Trackers.ContainsKey(_toolStripTracker.Name))
                        {
                            AppendHistoryItem(_toolStripTracker.CurrentHistoryItem);
                        }
                    };
                trackWorker.RunWorkerAsync();
            }
        }

        private void TrackUpdate(ICommand command)
        {
            _trackerService.TrackUpdate(command, _currentSong);
        }

        internal void TrackUpdate(ICommand command, Song song)
        {
            _trackerService.TrackUpdate(command, song);
        }

        private void TrackStop()
        {
            _trackerService.Stop();
        }

        private void TrackPause()
        {
            _trackerService.Pause();
        }

        private void TrackResume()
        {
            _trackerService.Resume();
        }
        #endregion

        #region 主窗口显示隐藏

        private readonly Point _hidePoint = new Point(-32000, -32000);

        private Point _originalLocation = new Point(100, 100);

        private void HideMainWindow()
        {
            if (Location != _hidePoint)
            {
                _originalLocation = Location;
            }
            Location = _hidePoint;
        }

        private void ShowMainWindow()
        {
            Location = _originalLocation;
            TopMost = FormTopMost;
        }
        #endregion

        #region 窗口停靠隐藏
        private Timer _dockTimer;

        private void InitialDock()
        {
            _dockTimer = new Timer { Enabled = false, Interval = 1500 };
            _dockTimer.Tick += DockTimerAutoHide_Tick;
        }

        private void SetDockLabel(String text)
        {
            dockPanel.DockText = text;
            dockPanel.UpdateDockLayout();
        }

        private void DockTimerAutoHide_Tick(object sender, EventArgs e)
        {
            FormAutoDock.SideHideOrShow(this, _dockTimer);
        }

        public void OnDock(DockType dockType)
        {
            switch (dockType)
            {
                case DockType.None:
                    if (dockPanel.Visible)
                    {
                        dockPanel.Visible = false;
                        FormBorderStyle = FormBorderStyle.FixedSingle;
                    }
                    break;
                case DockType.Left:
                    if (!dockPanel.Visible)
                    {
                        dockPanel.Dock = DockStyle.Right;
                        dockPanel.Width = FormAutoDock.DockSide;
                        dockPanel.Height = Height;
                        dockPanel.UpdateDockLayout();
                        dockPanel.Visible = true;
                        FormBorderStyle = FormBorderStyle.None;
                    }
                    break;
                case DockType.Right:
                    if (!dockPanel.Visible)
                    {
                        dockPanel.Dock = DockStyle.Left;
                        dockPanel.Width = FormAutoDock.DockSide;
                        dockPanel.Height = Height;
                        dockPanel.UpdateDockLayout();
                        dockPanel.Visible = true;
                        FormBorderStyle = FormBorderStyle.None;
                    }
                    break;
                case DockType.Top:
                    if (!dockPanel.Visible)
                    {
                        dockPanel.Dock = DockStyle.Bottom;
                        dockPanel.Width = Width;
                        dockPanel.Height = FormAutoDock.DockTop;
                        dockPanel.UpdateDockLayout();
                        dockPanel.Visible = true;
                        FormBorderStyle = FormBorderStyle.None;
                    }
                    break;
            }
        }
        #endregion

        private bool _contextMenuStripNotifyOpened = false;
        private bool _menuStripTopOpened = false;
        public bool DropdownOpen
        {
            get
            {
                return _menuStripTopOpened || _contextMenuStripNotifyOpened;
            }
        }

        #region 更新历史歌曲喜欢状态
        public void MakeFav(Song song, PostFresh refreshedFav)
        {
            if(song.Equals(_currentSong))
            {
                Execute((int)Command.Fav);
                if (refreshedFav != null)
                {
                    FreshInfo(0.2, refreshedFav);
                }
            }
            else if(_currentAccount.IsLoged && !String.IsNullOrEmpty(song.SongID))
            {
                if (_currentAccount.IsLoginWithID)
                {
                    MakeFaveWithWebWorker(song, refreshedFav);
                }
                else
                {
                    MakeFaveWithWebBrowser(song, refreshedFav);
                }
            }
        }

        private const String _favUrlForGet = "http://douban.fm/j/song/{0}/interest?action={1}&ck=PrQO";
        private const String _favUrlForPost = "http://douban.fm/j/song/{0}/interest";
        private void MakeFaveWithWebWorker(Song song, PostFresh refreshedFav)
        {
            IWebWorker webWorker = new DoubanWebWorker();
            switch (song.FaveState)
            {
                case FavState.Fav:
                    webWorker.AchieveResponse(String.Format(_favUrlForGet, song.SongID, "n"), null, _currentAccount.Cookie.CookieString, false, RequestMethod.Get);
                    break;
                case FavState.UnFav:
                    webWorker.AchieveResponse(String.Format(_favUrlForGet, song.SongID, "y"), null, _currentAccount.Cookie.CookieString, false, RequestMethod.Get);
                    break;
            }
            RefreshFave(song, refreshedFav);
        }

        private void MakeFaveWithWebBrowser(Song song, PostFresh refreshedFav)
        {
            WebBrowser favBrowser = new WebBrowser { ScriptErrorsSuppressed = false, Visible = false };
            favBrowser.DocumentCompleted += (dSender, dE) =>
            {
                favBrowser.Dispose();
                RefreshFave(song, refreshedFav);
            };
            switch (song.FaveState)
            {
                case FavState.Fav:
                    favBrowser.Navigate(String.Format(_favUrlForGet, song.SongID, "n"));
                    break;
                case FavState.UnFav:
                    favBrowser.Navigate(String.Format(_favUrlForGet, song.SongID, "y"));
                    break;
            }
        }

        private void RefreshFave(Song song, PostFresh refreshedFav)
        {
            song.FaveState = song.FaveState == FavState.Fav ? FavState.UnFav : FavState.Fav;
            TrackUpdate(CommandFactory.GetCommand(Command.Fav, RadioType.PrivateRadio), song);
            RadioUtility.ReleaseMemory();
            if (refreshedFav != null)
            {
                refreshedFav(ref song);
            }
            PopInfo(song.FaveState == FavState.Fav ? String.Format("{1}：{0}", song.Title, StringResource.Command_Like) : String.Format("{1}：{0}", song.Title, StringResource.Command_Unlike));
            
        }
        #endregion

        #region 窗口关闭
        private readonly ClosingForm _closingForm = new ClosingForm();
        private void DoubanForm_Closing(object sender, CancelEventArgs e)
        {
            if (!_realClose)
            {
                switch (_settings.CloseOption)
                {
                    case 0:
                        var result = _closingForm.ShowDialog();
                        switch (result)
                        {
                            case DialogResult.OK:
                                break;
                            case DialogResult.Yes:
                                e.Cancel = true;
                                HideToNotify();
                                break;
                            default:
                                e.Cancel = true;
                                break;
                        }
                        if (_closingForm.ConfirmResult)
                        {
                            switch (result)
                            {
                                case DialogResult.OK:
                                    _settings.CloseOption = 1;
                                    break;
                                case DialogResult.Yes:
                                    _settings.CloseOption = 2;
                                    break;
                            }
                            _settings.Save();
                        }
                        break;
                    case 1:
                        break;
                    case 2:
                        e.Cancel = true;
                        HideToNotify();
                        break;
                }
            }
        }
        #endregion
    }
}